/*
 *  $Id: ScrollBar.cs 1232 2010-12-07 13:16:37Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008-2010 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;

using AgateLib.DisplayLib;
using AgateLib.Geometry;
using AgateLib.InputLib;

using Nereal.Serialization;

using Ragima.Helper;

namespace Ragima.UI {
    /// <summary>
    /// Абстрактный скроллер: виджет, управляющий скроллированием другого виджета.
    /// </summary>
    [TypeId("scrollbar")]
    public abstract class ScrollBar : Widget, IScrollable {
        private static readonly Color DefaultScrollBackColor = 0x408080.ToColor();
        private static readonly Color DefaultScrollTrackColor = 0x80FFFF.ToColor();
        private const int DefaultTrackSize = 10;

        private IScrollable _scrollTarget;
        private int _trackSize;

        public ScrollBar() {
            _scrollTarget = null;
            _trackSize = DefaultTrackSize;
        }

        /// <summary>
        /// Фоновый цвет по умолчанию для скроллера.
        /// </summary>
        protected override Nullable<Color> DefaultBackColor {
            get { return DefaultScrollBackColor; }
        }

        /// <summary>
        /// Скроллируемый объект.
        /// </summary>
        [XmlName("target"), InterfaceReference(typeof(Widget), "widget", false, typeof(string), "scene")]
        public IScrollable ScrollTarget {
            get { return _scrollTarget; }
            set { _scrollTarget = value; }
        }

        /// <summary>
        /// Размер ползунка скроллера.
        /// </summary>
        [XmlName("trackSize"), DefaultValue(DefaultTrackSize)]
        public int TrackSize {
            get { return _trackSize; }
            set { _trackSize = value; }
        }

        /// <summary>
        /// Цвет ползунка скроллера.
        /// </summary>
        private Color TrackColor {
            get { return MainColor ?? DefaultScrollTrackColor; }
        }

        /// <summary>
        /// Положение ползунка скроллера.
        /// </summary>
        protected int TrackPosition {
            get { return ScrollTarget != null ? GetTrackPosition(ScrollTarget.Position) : 0; }
        }

        /// <summary>
        /// Размер области скроллера.
        /// </summary>
        protected abstract int ScrollAreaSize { get; }
        /// <summary>
        /// Область ползунка.
        /// </summary>
        protected abstract Rectangle TrackRect { get; }
        /// <summary>
        /// Смещение курсора мыши от начала скроллера.
        /// </summary>
        protected abstract int MouseOffset { get; }

        /// <summary>
        /// Размер обрасти скроллера без ползунка.
        /// </summary>
        private int ScrollArea {
            get { return ScrollAreaSize - TrackSize; }
        }

        /// <summary>
        /// Получает положение ползунка.
        /// </summary>
        private int GetTrackPosition(int position) {
            if (ScrollTarget == null || ScrollTarget.MaxPosition == 0)
                return 0;
            return ScrollArea * position / ScrollTarget.MaxPosition;
        }

        /// <summary>
        /// Получает позицию в скроллере по смещению курсора мыши.
        /// </summary>
        private int GetPositionByMouse(int pos) {
            if (ScrollTarget == null || ScrollTarget.MaxPosition == 0 || pos < 0)
                return 0;
            int size = ScrollArea;
            int max = ScrollTarget.MaxPosition;
            if (pos >= size)
                return max;
            return pos * max / size;
        }

        /// <summary>
        /// Рисует скроллер.
        /// </summary>
        public override void Draw() {
            DrawBackground();
            if (ScrollTarget == null)
                return;
            var trackRect = TrackRect;
            var loc = AbsoluteLocation;
            trackRect.X += loc.X;
            trackRect.Y += loc.Y;
            Display.FillRect(trackRect, TrackColor);
        }

        /// <summary>
        /// Обрабатывает перемещение курсора мыши.
        /// </summary>
        public override void DoMouseMove(Point point) {
            if (ScrollTarget == null)
                return;
            var scene = ParentScene;
            if (scene != null && scene.DragStartWidget == this) {
                int index = GetPositionByMouse(MouseOffset);
                if (index != ScrollTarget.Position)
                    ScrollTarget.ScrollTo(index);
            }
        }

        /// <summary>
        /// Обрабатывает клики по скроллеру.
        /// </summary>
        public override void DoClick(Mouse.MouseButtons buttons, bool down) {
            if (ScrollTarget == null)
                return;
            if (buttons != Mouse.MouseButtons.Primary)
                return;
            if (down) {
                int pos = MouseOffset;
                int index = GetPositionByMouse(pos);
                
                if (index != ScrollTarget.Position) {
                    ScrollTarget.ScrollTo(index);
                } else if (pos < TrackPosition) {
                    ScrollTarget.ScrollBy(-1);
                } else if (pos >= (TrackPosition + TrackSize)) {
                    ScrollTarget.ScrollBy(1);
                } else {
                    var scene = ParentScene;
                    if (scene != null)
                        scene.StartDrag(this);
                }
            } else {
                var scene = ParentScene;
                if (scene != null && scene.DragStartWidget == this)
                    scene.EndDrag();
            }
        }

        #region IScrollable implementation
        public int Position {
            get { return ScrollTarget != null ? ScrollTarget.Position : 0; }
        }

        public int MaxPosition {
            get { return ScrollTarget != null ? ScrollTarget.MaxPosition : 0; }
        }

        public void ScrollBy(int offset) {
            if (ScrollTarget != null)
                ScrollTarget.ScrollBy(offset);
        }

        public void ScrollTo(int index) {
            if (ScrollTarget != null)
                ScrollTarget.ScrollTo(index);
        }
        #endregion
    }

    /// <summary>
    /// Вертикальный скроллер.
    /// </summary>
    [TypeId("vscroll")]
    public class VScrollBar : ScrollBar {
        protected override int ScrollAreaSize {
            get { return Height; }
        }

        protected override Rectangle TrackRect {
            get { return new Rectangle(0, TrackPosition, Width, TrackSize); }
        }

        protected override int MouseOffset {
            get { return MouseLocation.Y; }
        }
    }

    /// <summary>
    /// Горизонтальный скроллер.
    /// </summary>
    [TypeId("hscroll")]
    public class HScrollBar : ScrollBar {
        protected override int ScrollAreaSize {
            get { return Width; }
        }

        protected override Rectangle TrackRect {
            get { return new Rectangle(TrackPosition, 0, TrackSize, Height); }
        }

        protected override int MouseOffset {
            get { return MouseLocation.X; }
        }
    }
}
